package connection

import (
	"encoding/json"
	"runtime/debug"
	"unsafe"

	"chat.com/common"
	"chat.com/library"
	"chat.com/pkg/logger"
	"github.com/gorilla/websocket"
)

var log *logger.Logger = logger.NewLogger("pkg/logger/connection.log", "")

const (
	// 用户连接超时时间
	heartbeatExpirationTime = 300
)

// Connection 用于管理单个游客或用户的websocket连接
type Connection struct {
	Addr             string          // 客户端地址
	WsConn           *websocket.Conn // 用户连接
	PreAcceptedData  chan []byte     // 待接收的数据
	PreAcceptedAck   chan []byte     // 待接收的ack
	PreAcceptedError chan []byte     // 待接收的error
	AppId            int64           // 登录的平台Id app/web/ios
	UserId           int64           // 用户Id，用户登录以后才有
	FirstTime        uint64          // 首次连接事件
	HeartbeatTime    uint64          // 用户上次心跳时间
	LoginTime        uint64          // 登录时间 登录以后才有
	AuthType         int64           // 是游客还是用户, 0 游客 1 用户
}

// Connection 初始化
func NewConnection(addr string, socket *websocket.Conn, firstTime uint64, authType int64) (connection *Connection) {
	connection = &Connection{
		Addr:             addr,
		WsConn:           socket,
		PreAcceptedData:  make(chan []byte, 100),
		PreAcceptedAck:   make(chan []byte, 100),
		PreAcceptedError: make(chan []byte, 100),
		FirstTime:        firstTime,
		HeartbeatTime:    firstTime,
		AuthType:         authType,
	}

	return
}

// 读取客户端请求数据, 独立协程后台运行
func (c *Connection) Read() {
	defer func() {
		if r := recover(); r != nil {
			logger.Debug("读取客户端数据 关闭send", c)
			close(c.PreAcceptedData)
			logger.Debug("read stop %s, %s", string(debug.Stack()), r)
		}
	}()

	for {
		_, message, err := c.WsConn.ReadMessage()
		if err != nil {
			logger.Error("读取客户端数据时出错: %s, %s", c.Addr, err)
			continue
		}

		logger.Debug("读取客户端数据进行处理: %s", string(message))
		c.processRequest(message)
	}
}

// 处理客户端请求数据
func (c *Connection) processRequest(message []byte) {
	logger.Debug("processRequest开始处理数据: %s %s", c.Addr, string(message))

	defer func() {
		if r := recover(); r != nil {
			logger.Debug("processRequest处理数据时崩溃: %s", r)
		}
	}()

	request := &common.WsRequest{}
	if err := json.Unmarshal(message, request); err != nil {
		logger.Debug("processRequest将请求数据转化common.WsRequest时出错: %s", err)
		c.SendError([]byte("数据不合法"))
		return
	}

	requestData, err := json.Marshal(request.Data)
	if err != nil {
		logger.Debug("processRequest处理数据出错: %s", err)
		c.SendError([]byte("处理数据失败"))
		return
	}

	var (
		code int64
		msg  string
		data interface{}
	)

	// 请求的命令
	cmd := request.Cmd

	// 根据请求的命令获取对应处理函数
	if handleFunc, ok := GetHandler(cmd); !ok {
		code = common.RoutingNotExist
		logger.Error("processRequest处理时出错: 命令不存在 client %s, userId %d, cmd %s", c.Addr, c.UserId, cmd)
	} else {
		// 执行请求
		code, msg, data = handleFunc(c, requestData)
	}

	msg = common.GetErrorMessage(code, msg)
	wsResponse := common.NewWsResponse(cmd, code, msg, data)
	headByte, err := json.Marshal(wsResponse)
	if err != nil {
		logger.Debug("处理数据 json Marshal %s", err)
		return
	}

	//c.respond(code, headByte)

	logger.Debug("acc_response send", c.Addr, c.AppId, c.UserId, "cmd", cmd, "code", code)
}

// 回复客户端响应数据
func (c *Connection) Respond(code int64, data []byte) {
	switch code {
	case common.OK:
		c.SendAck(data)
	default:
		c.SendError(data)
	}
}

// 向客户端写数据, 独立协程后台运行
func (c *Connection) Write() {

	defer func() {
		if r := recover(); r != nil {
			c.WsConn.Close()
			switch c.AuthType {
			case 0: // 游客
				ConnectionManagerInstance.UnregisterVisitorCh <- c
			case 1: // 用户
				ConnectionManagerInstance.LogoutCh <- c
			}
			logger.Debug("write stop", string(debug.Stack()), r)
		}
	}()

	for {
		select {
		// 接收消息
		case message, ok := <-c.PreAcceptedData:
			if !ok {
				// 发送数据错误 关闭连接
				log.Error("Client发送数据错误, 关闭连接: %s, user: %d, SendData: %v\n", c.Addr, c.UserId, ok)
				return
			}

			log.Debug("starts writing message to client %d, data: %s\n", c.UserId, message)
			if err := c.WsConn.WriteMessage(websocket.TextMessage, message); err != nil {
				panic(err)
			}

		// 接收ack
		case ack, ok := <-c.PreAcceptedAck:
			if !ok {
				// 发送数据错误 关闭连接
				log.Error("Client发送数据错误, 关闭连接: %s, user: %d, SendData: %v\n", c.Addr, c.UserId, ok)
				return
			}

			log.Debug("starts writing message to client %d, data: %s\n", c.UserId, ack)
			if err := c.WsConn.WriteMessage(websocket.TextMessage, ack); err != nil {
				panic(err)
			}

		// 接收error
		case ack, ok := <-c.PreAcceptedError:
			if !ok {
				// 发送数据错误 关闭连接
				log.Error("Client发送数据错误, 关闭连接: %s, user: %d, SendData: %v\n", c.Addr, c.UserId, ok)
				return
			}

			log.Debug("starts writing message to client %d, data: %s\n", c.UserId, ack)
			if err := c.WsConn.WriteMessage(websocket.TextMessage, ack); err != nil {
				panic(err)
			}
		}
	}
}

// SendMessage 给当前用户发送消息
func (c *Connection) SendMessage(msg []byte) {
	if c == nil {
		return
	}

	defer func() {
		if r := recover(); r != nil {
			logger.Debug("SendMsg stop:", r, string(debug.Stack()))
		}
	}()

	c.PreAcceptedData <- msg
}

// SendAck 给当前用户发送ack
func (c *Connection) SendAck(ack []byte) {
	if c == nil {
		return
	}

	defer func() {
		if r := recover(); r != nil {
			logger.Debug("SendMsg stop:", r, string(debug.Stack()))
		}
	}()

	c.PreAcceptedAck <- ack
}

// SendError 给当前用户发送error
func (c *Connection) SendError(errInfo []byte) {
	if c == nil {
		return
	}

	defer func() {
		if r := recover(); r != nil {
			logger.Debug("SendMsg stop:", r, string(debug.Stack()))
		}
	}()

	c.PreAcceptedError <- errInfo
}

// SendOfflineMsg 给当前用户发送离线消息数据
func (c *Connection) SendOfflineMsg() {
	if c == nil {
		return
	}

	redisOfflineSingleChatMsg := library.GetOfflineSingleChatMsg(c.UserId)
	redisOfflineGroupChatMsg := library.GetOfflineGroupChatMsg(c.UserId)
	if len(redisOfflineSingleChatMsg) == 0 && len(redisOfflineGroupChatMsg) == 0 {
		return
	}

	offlineSingleChatMsg, _ := json.Marshal(redisOfflineSingleChatMsg)
	offlineGroupChatMsg, _ := json.Marshal(redisOfflineGroupChatMsg)

	defer func() {
		if r := recover(); r != nil {
			logger.Debug("SendOfflineMsg stop:", r, string(debug.Stack()))
		}
	}()

	c.PreAcceptedData <- offlineSingleChatMsg
	c.PreAcceptedData <- offlineGroupChatMsg
}

// close 关闭客户端连接
func (c *Connection) Close() {
	close(c.PreAcceptedData)
}

// 用户登录
func (c *Connection) Login(appId int64, userId int64, loginTime uint64) {

	c.AppId = appId
	c.UserId = userId
	c.LoginTime = loginTime
	// 登录成功=心跳一次
	c.Heartbeat(loginTime)
}

// 用户心跳
func (c *Connection) Heartbeat(currentTime uint64) {
	c.HeartbeatTime = currentTime
	return
}

// 心跳超时
func (c *Connection) IsHeartbeatTimeout(currentTime uint64) (timeout bool) {
	if c.HeartbeatTime+heartbeatExpirationTime <= currentTime {
		timeout = true
	}
	return
}

// 是否登录了
func (c *Connection) IsLogin() (isLogin bool) {
	// 用户登录了
	if c.UserId > 0 {
		isLogin = true
		return
	}
	return
}

// Sizeof 获取连接占内存大小
func (c *Connection) Size() (size int64) {
	size1 := unsafe.Sizeof(*c)
	size = int64(size1)
	return
}

// GetKey 获取 key
func (c *Connection) GetKey() (key string) {
	key = library.GetUserKey(c.AppId, c.UserId)
	return
}
